﻿using System;
using System.Collections.Generic;
using UnityEngine;
using XLua;
using Yoozoo.Libs;
using Yoozoo.Managers;
using Yoozoo.Managers.ResourceManagerV2.Runtime;
using Yoozoo.Mars.Got;
using Yoozoo.Mars.Got.SpriteAnim;
using Object = UnityEngine.Object;

namespace Yoozoo.Modules.InnerCity
{
    public class CityUnitType
    {
        public const string Guard = "guard";
        public const string Patrol = "patrol";
        public const string Person = "person";

        public static string ParseType(int type)
        {
            switch (type)
            {
                case 1:
                    return Guard;

                case 2:
                    return Patrol;

                case 3:
                    return Person;

                default:
                    return string.Empty;
            }
        }
    }

    // transform 信息
    public class CityUnitData
    {
        public Vector3 position;
        public Vector3 rotation;
        public float scale;

        public CityUnitData(Vector3 position, Vector3 rotation, float scale)
        {
            this.position = position;
            this.rotation = rotation;
            this.scale = scale;
        }
    }

    public enum CallLuaOperationCode
    {
        Empty = 0,
        PersonArrivePoint = 1,
        PersonIdleEnd = 2,
        VectorToGrid = 3,
        GridToVector = 4,
        FindPath = 5,
    }

    public class CallLuaArg : IClass
    {
        public CallLuaOperationCode code;
        public object arg1;
        public object arg2;
        public object arg3;

        public void OnReset()
        {
            this.code = CallLuaOperationCode.Empty;
            this.arg1 = null;
            this.arg2 = null;
            this.arg3 = null;
        }
    }

    public class CityUnitMgr
    {
        public const float unitWalkSpeed = 0.0225f;

        private static CityUnitMgr instance;

        public static CityUnitMgr Instance
        {
            get
            {
                if (instance == null)
                    instance = new CityUnitMgr();

                return instance;
            }
        }

        private SpriteParticleSystemManager particleSysMgr;
        public SpriteParticleSystemManager ParticleSystemManager => particleSysMgr;

        private bool isResReady;
        private ResLoader resLoader;
        private Dictionary<int, CityUnitGroup> groupDict;
        private CityUnitMgrEntity myBaseObj;
        private LuaFunction luaFunction;

        private const string POSITION_KEY = "position";
        private const string ROTATION_KEY = "rotation";
        private const string SCALE_KEY = "scale";

        #region Lua调用

        public static void Initialize(LuaTable table)
        {
            Instance._Initialize(table);
        }

        public static void Destory()
        {
            Instance._Destroy();
        }
        public static CityUnitGroup CreateCityGroup(LuaTable unitsInfo)
        {
            return Instance._CreateCityGroup(unitsInfo);
        }

        public static Vector3 GetPersonPos(int personID, int cityID = 1)
        {
            return Instance._GetPersonPos(cityID, personID);
        }

        public static Vector3 GetUnitPos(int type, int id)
        {
            return Instance._GetUnitPos(1, CityUnitType.ParseType(type), id);
        }

        public static void PublishWalkCommand(LuaTable walkInfo)
        {
            Instance._PublishWalkCommand(walkInfo);
        }

        public static void PublishBuildCommand(LuaTable buildInfo)
        {
            Instance._PublishBuildCommand(buildInfo);
        }

        public static void PublishDismissBuildCommand(int id)
        {
            Instance._PublishDismissBuildCommand(id);
        }

        public static void DestroyCityGroup()
        {
            Instance._DestroyCityGroup();
        }

        public static void PublishPatrolCommand(int id, List<Vector3> path)
        {
            Instance._PublishPatrolCommand(id, path);
        }

        public static void PublishBuildPosChange(LuaTable table)
        {
            Instance._PublishBuildPosChange(table);
        }

        public static void PublishStorageChange(LuaTable table)
        {
            Instance._PublishStoragePosChange(table);
        }

        public static void PublishResPosChange(LuaTable table)
        {
            Instance._PublishResPosChange(table);
        }

        public static int GetRandomUnit()
        {
            return Instance._GetRandomUnit();
        }

        public static void PublicStopMoveCommand(int type, int id)
        {
            Instance._PublishUnitStopMove(type, id);
        }

        public static void PublicContinueMoveCommand(int type, int id)
        {
            Instance._PublishUnitContinueMove(type, id);
        }

        #endregion

        #region C#调用

        public AnimSprite[] GetSprite(string modelName, int count)
        {
            var creator = this.particleSysMgr.GetSpriteCreatorByName(GetCreatorName(modelName));
            return this.particleSysMgr.CreateAnimSprites(creator, count);
        }

        public void PlaySpriteAnimation(AnimSprite sprite, SPRITE_ACTION_ENUM action)
        {
            this.particleSysMgr.PlayActionAnimation(sprite, action);
        }

        public void Update()
        {
            if(this.groupDict != null)
                foreach (var kvp in this.groupDict)
                    kvp.Value.Update();
        }

        public void CallLuaWithoutResult(CallLuaArg arg)
        {
            this.luaFunction?.Action(arg);
        }

        public T CallLuaWithResult<T>(CallLuaArg arg)
        {
            if (this.luaFunction == null)
                return default(T);

            return luaFunction.Func<CallLuaArg, T>(arg);
        }

        public Vector3 GridToVector(Vector2Int v2)
        {
            var callArg = ClassManager.Get<CallLuaArg>();
            callArg.code = CallLuaOperationCode.GridToVector;
            callArg.arg1 = v2.x;
            callArg.arg2 = v2.y;

            return CallLuaWithResult<Vector3>(callArg);
        }

        public Vector2Int VectorToGrid(Vector3 v3)
        {
            var callArg = ClassManager.Get<CallLuaArg>();
            callArg.code = CallLuaOperationCode.VectorToGrid;
            callArg.arg1 = v3.x;
            callArg.arg2 = v3.y;
            callArg.arg3 = v3.z;

            var table = CallLuaWithResult<LuaTable>(callArg);
            if (table == null)
                return Vector2Int.zero;

            return new Vector2Int(table.Get<int>("x"), table.Get<int>("y"));
        }

        public List<Vector3> FindPath(Vector2Int start, Vector2Int end)
        {
            var callArg = ClassManager.Get<CallLuaArg>();
            callArg.code = CallLuaOperationCode.FindPath;
            callArg.arg1 = start;
            callArg.arg2 = end;

            return CallLuaWithResult<List<Vector3>>(callArg);
        }

        public void RemoveSprite(AnimSprite sprite)
        {
            this.particleSysMgr.RemoveSprite(sprite);
        }

        public List<Vector2Int> ParseIndexToGridPos(float[] index, int gridW)
        {
            if (index == null || index.Length == 0)
                return null;

            var ret = new List<Vector2Int>(index.Length);
            foreach (var pos in index)
            {
                var _pos = (int) pos;
                var x = _pos % gridW;
                var y = _pos / gridW;
                ret.Add(new Vector2Int(x, y));
            }

            return ret;
        }

        #endregion

        private void _Initialize(LuaTable table)
        {
            _go = new GameObject("CityUnitMgrEntity");
            var sceneRoot = GameObject.Find("SceneRoot");
            if (sceneRoot != null)
                _go.transform.SetParent(sceneRoot.transform, false);

            this.myBaseObj = _go.AddComponent<CityUnitMgrEntity>();
            this.myBaseObj.SetBody(this);

            this.isResReady = false;
            this.resLoader = ResLoader.Alloc();

            //var particleSysGo = new GameObject("SpriteParticleSystemManager");
            //this.particleSysMgr = particleSysGo.AddComponent<SpriteParticleSystemManager>();
            //particleSysGo.transform.SetParent(_go.transform, false);
            //this.particleSysMgr.InitManager(new CityAISpriteTemplate(), BaseSpriteCache.Instance, () =>
            //{
            //    LoadRes(() => { });
            //});

            this.luaFunction = table.GetLuaFunction("luaFunc");
        }

        private void _Destroy()
        {
            this.resLoader.Recycle2Cache();
            this.resLoader = null;

            GameObject.Destroy(_go);
            _go = null;
            this.myBaseObj = null;
            this.luaFunction = null;
            this.particleSysMgr.Dispose();
            this.particleSysMgr = null;
        }
        // 创建unit
        private CityUnitGroup _CreateCityGroup(LuaTable unitsInfo)
        {
            var count = 0;
            var guardData = ParseCityUnitData(
                unitsInfo.Get<LuaTable>(CityUnitType.Guard));
            if (guardData != null)
                count++;

            var patrolData = ParseCityUnitData(
                unitsInfo.Get<LuaTable>(CityUnitType.Patrol));
            if (patrolData != null)
                count++;

            var personData = ParseCityUnitData(
                unitsInfo.Get<LuaTable>(CityUnitType.Person));
            if (personData != null)
                count++;

            if (count != 3)
                return null;

            var ret = ClassManager.Get<CityUnitGroup>();
            ret.Initialize(guardData, patrolData, personData);
            if (this.groupDict == null)
                this.groupDict = new Dictionary<int, CityUnitGroup>();

            this.groupDict.Add(1, ret);
            return ret;
        }

        // 解析创建unit的lua数据
        private Dictionary<int, CityUnitData> ParseCityUnitData(LuaTable rawData)
        {
            if (rawData == null || rawData.Length == 0)
                return null;

            var ret = new Dictionary<int, CityUnitData>();
            for (var i = 1; i <= rawData.Length; i++)
            {
                var data = rawData.Get<LuaTable>(i);
                if (data == null)
                    continue;

                if (!data.ContainsKey(POSITION_KEY))
                    continue;

                var p = data.Get<Vector3>(POSITION_KEY);

                var r = Vector3.zero;
                if (data.ContainsKey(ROTATION_KEY))
                    r = data.Get<Vector3>(ROTATION_KEY);

                var s = 0.015f;
                if (data.ContainsKey(SCALE_KEY))
                    s = data.Get<float>(SCALE_KEY);

                ret.Add(i, new CityUnitData(p, r, s));
            }

            return ret;
        }

        private string GetCreatorName(string atlasName)
        {
            return string.Format("creator_{0}", atlasName);
        }

        private void LoadRes(Action onLoadOver)
        {
            this.onLoadFinally = () =>
            {
                this.isResReady = true;
                onLoadOver?.Invoke();
            };

            LoadRes2();
        }

        private Material marchMat;
        private int resCount;
        private Action onLoadFinally;
        private GameObject _go;

        public Material Material
        {
            get
            {
                return this.marchMat;
            }
        }

        private void LoadRes2()
        {
            this.resCount = 2;
            this.resLoader.Add2Load("Assets/ResourcesAssets/Materials/mat_CitySprite.mat", (succ, name, asset) =>
            {
                this.marchMat = Object.Instantiate(asset as Object) as Material;
                //this.particleSysMgr.CreateSpriteCreator("gta_jeep1", GetCreatorName("gta_jeep1"),
                //    "Assets/ResourcesAssets/FrameAnimation/mat_world_march.mat", OnCreateCreatorOver);

                //this.particleSysMgr.CreateSpriteCreator("gta_sniper1", GetCreatorName("gta_sniper1"),
                //    "Assets/ResourcesAssets/FrameAnimation/mat_world_march.mat", OnCreateCreatorOver);
                OnCreateCreatorOver();
            }).Load();
        }

        private void OnCreateCreatorOver()
        {
            resCount--;
            if (resCount <= 0)
                this.onLoadFinally?.Invoke();
        }

        private void _PublishWalkCommand(LuaTable walkInfo)
        {
            GetGroup(1)?.PublishWalkCommand(ParseWalkInfo(walkInfo));
        }

        private Dictionary<string, Dictionary<int, List<Vector3>>> ParseWalkInfo(LuaTable walkInfo)
        {
            var ret = new Dictionary<string, Dictionary<int, List<Vector3>>>();
            var objKeys = walkInfo.GetKeys();
            foreach (var objKey in objKeys)
            {
                var key = (string) objKey;
                var table = walkInfo.Get<LuaTable>(key);
                if (table == null)
                    continue;

                var dict = new Dictionary<int, List<Vector3>>();
                ret.Add(key, dict);

                var keysObj1 = table.GetKeys();
                foreach (var keyObj1 in keysObj1)
                {
                    var key1 = (long) keyObj1;
                    var list = table.Get<long, List<Vector3>>(key1);
                    if (list == null)
                        continue;

                    dict.Add((int) key1, list);
                }
            }

            return ret;
        }

        private CityUnitGroup GetGroup(int id)
        {
            if (this.groupDict == null)
                return null;

            CityUnitGroup ret;
            if (!this.groupDict.TryGetValue(id, out ret))
                return null;

            return ret;
        }

        private Vector3 _GetPersonPos(int cityID, int unitID)
        {
            return _GetUnitPos(cityID, CityUnitType.Person, unitID);
        }

        private Vector3 _GetUnitPos(int cityID, string type, int unitID)
        {
            var group = GetGroup(cityID);
            return group == null ? Vector3.zero : group.GetUnitPos(type, unitID);
        }

        private void _PublishBuildCommand(LuaTable buildInfo)
        {
            GetGroup(1)?.PublishBuildCommand(buildInfo);
        }

        private void _PublishDismissBuildCommand(int id)
        {
            GetGroup(1)?.PublishDismissBuildCommand(id);
        }

        private void _DestroyCityGroup()
        {
            var group = GetGroup(1);
            if (group == null)
                return;

            this.groupDict.Remove(1);
            ClassManager.Free(group);
        }

        private void _PublishPatrolCommand(int id, List<Vector3> path)
        {
            GetGroup(1)?.PublishPatrolCommand(id, path);
        }

        private void _PublishBuildPosChange(LuaTable table)
        {
            var group = GetGroup(1);
            if (group == null)
                return;

            var id = table.Get<int>("id");
            var width = table.Get<int>("gridW");
            var area = table.Get<float[]>("area");
            var grids = ParseIndexToGridPos(area, width);
            group.PublishBuildPosChange(id, grids);
        }

        private void _PublishStoragePosChange(LuaTable table)
        {
            var group = GetGroup(1);
            if (group == null)
                return;

            var width = table.Get<int>("gridW");
            var area = table.Get<float[]>("area");
            var grids = ParseIndexToGridPos(area, width);
            group.PublishStoragePosChange(grids);
        }

        private void _PublishResPosChange(LuaTable table)
        {
            var group = GetGroup(1);
            if (group == null)
                return;

            var resType = table.Get<int>("type");
            var index = table.Get<int>("index");
            var width = table.Get<int>("gridW");
            var area = table.Get<float[]>("area");
            var grids = ParseIndexToGridPos(area, width);
            group.PublishResPosChange(resType, index, grids);
        }

        private int _GetRandomUnit()
        {
            var group = GetGroup(1);
            return group == null ? 0 : group.GetRandomUnit();
        }

        private void _PublishUnitStopMove(int type, int id)
        {
            GetGroup(1)?.UnitStopMove(CityUnitType.ParseType(type), id);
        }

        private void _PublishUnitContinueMove(int type, int id)
        {
            GetGroup(1)?.UnitContinueMove(CityUnitType.ParseType(type), id);
        }
    }
}
